package org.noote.roby.core;

import org.noote.libs.bluetooth.SPPClient;
import org.noote.libs.camera.CameraInterface;
import org.noote.libs.camera.helper.Helper_Camera;
import org.noote.libs.graphicbuffer.GraphicBuffer;
import org.noote.libs.roby.comm.BluetoothCommandEvents;
import org.noote.libs.roby.comm.BluetoothCommandProtocol;
import org.noote.libs.roby.comm.IPServerCommandEvents;
import org.noote.libs.roby.comm.IPServerCommandProtocol;
import org.noote.libs.roby.configuration.PositionMarker;
import org.noote.libs.roby.configuration.RobotSettings;
import org.noote.libs.roby.configuration.RobotStates;
import org.noote.libs.roby.scan.ScanBitmapFactory;
import org.noote.libs.roby.scan.ScanResult;
import org.noote.libs.tcpip.helper.Helper_WiFi;
import org.noote.libs.tcpip.IPEvent_Server;
import org.noote.libs.tcpip.IPServer;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.util.Log;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.Toast;

public class RobotCore_Activity extends Activity {
	private static final String LOG_TAG = "RobotCore_Activity";	

	private String m_sCoreName = "NooTe's Robot";
	private String m_sCoreVersion = "0.2";
	private String m_sCurrentIPAddress = "0.0.0.0";
	
	myIPServer m_IPServer = new myIPServer(null);
	private int m_iServerPort = 8086;
	
	private CameraInterface m_camera = new CameraInterface();
	private CameraPreview m_viewCamera = null;
	private PositionSensor m_PositionSensor = null;
	
	private WorldView m_viewWorld = null;
	
    String sWorkPath = "/RobotController";
    
	private String m_sBTAddressDevice;
	SPPClient m_SPPClient = null;
	
	int _iReadyStep = 0;
	RobotSettings _settings = new RobotSettings();
	RobotStates _states = new RobotStates();
	
	protected ScanBitmapFactory _scanBitmapFactory = new ScanBitmapFactory();
	protected ScanResult _latest_scan = null;

	boolean _bInScanMode = false;
	protected static final int SCANPROGRESS_START = 0;
	protected static final int SCANPROGRESS_MSG = 1;
	protected static final int SCANPROGRESS_FINISH = 2;
	protected ProgressDialog m_mScanProgressDialog = null;

	PowerManager.WakeLock wl = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.core);

		m_SPPClient = new SPPClient(mBTEvents);
		
        sWorkPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/RobotController";
        
		loadSettings();
		
        getCurrentIPAddress();
        
        // Register for broadcasts when a device is disconnected !
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
        this.registerReceiver(mReceiver, filter);

		// Launch IP server
        if(!m_IPServer.isConnected())
        {
        	Log.d(LOG_TAG, "Launch ip server on "+m_sCurrentIPAddress+":"+m_iServerPort+"...");
        	if(!m_IPServer.create(m_iServerPort))
        	{
        		Log.d(LOG_TAG, "Error then start server :(");
				Toast.makeText(this, "Error then start server :(", Toast.LENGTH_LONG).show();    	
				setDisplayMessage("Error then start server :(");
        	}
        }
        
		// Launch Bluetooth client
		if(!m_SPPClient.isConnected())
		{
			setDisplayMessage("Connection bluetooth at "+m_sBTAddressDevice+"...");
			if(!m_SPPClient.connect(m_sBTAddressDevice))
			{
        		Log.d(LOG_TAG, "Cannot connect bluetooth :(");
				Toast.makeText(this, "Cannot connect bluetooth :(", Toast.LENGTH_LONG).show();    	
				setDisplayMessage("Cannot connect bluetooth :(");
			}
		}

        EditText editCurrentIP = (EditText) findViewById(R.id.editCurrentIP);
		editCurrentIP.setText(m_sCurrentIPAddress + ":" + String.valueOf(m_iServerPort));
		
		m_viewWorld = new WorldView(this);
		((FrameLayout) findViewById(R.id.layoutWorld)).addView(m_viewWorld);
		
		if(!m_camera.open(Helper_Camera.getCamera()))
		{
			Toast.makeText(getApplicationContext(), "Cannot start camera !", Toast.LENGTH_LONG).show();
			return;
		}

		m_viewCamera = new CameraPreview(this, m_camera, m_viewWorld);
		((FrameLayout) findViewById(R.id.layoutPreview)).addView(m_viewCamera);
		
		m_PositionSensor = new PositionSensor(this);
		m_PositionSensor.refreshLayout();
	}
	
	@Override
	public void onResume()
	{
		super.onResume();
		
		if(wl == null)
		{
			PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
		    wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "Core Robot");
		}
		if(wl != null ) wl.acquire();
	}
	
	@Override
	public void onPause()
	{
		super.onPause();
		
		if(wl != null ) wl.release();
	}

	private void loadSettings() {
        SharedPreferences settings = getSharedPreferences(Main_Activity.PREFS_NAME, 0);
        m_iServerPort = Integer.valueOf(settings.getString(Main_Activity.PREFS_NAME_SERVER_PORT, String.valueOf(m_iServerPort)));
        m_sCoreName = settings.getString(Main_Activity.PREFS_NAME_CORE_NAME, m_sCoreName);
        m_sBTAddressDevice = settings.getString(Main_Activity.PREFS_BT_ADDRESS_DEVICE, m_sBTAddressDevice);

        boolean bNeedReSaving = false;
        if(m_iServerPort<1) { m_iServerPort = 8086; bNeedReSaving=true; }
        else if(m_iServerPort>65535) { m_iServerPort = 65535; bNeedReSaving=true; }
        if(m_sCoreName.length() == 0) { m_sCoreName = "NooTe's Robot"; bNeedReSaving=true; }
        
        if(bNeedReSaving)
        {
        	SharedPreferences.Editor editor = settings.edit();
        	editor.putString(Main_Activity.PREFS_NAME_SERVER_PORT, String.valueOf(m_iServerPort));
        	editor.putString(Main_Activity.PREFS_NAME_CORE_NAME, m_sCoreName);
        	editor.putString(Main_Activity.PREFS_BT_ADDRESS_DEVICE, m_sBTAddressDevice);
        	
        	editor.commit();
        }
	}
	
	private void getCurrentIPAddress() {
		m_sCurrentIPAddress = Helper_WiFi.getCurrentIPAddress_WiFi_Only(this, true);
		if(m_sCurrentIPAddress == "")
		{
			new AlertDialog.Builder(this)
			.setTitle("Not active connexion detected...")
			.setPositiveButton("Ok",
					new DialogInterface.OnClickListener() {
						@Override
						public void onClick(DialogInterface dialog,
								int whichButton) {
							finish();
						}
					}).show();
			return;
		}
	}
	
	protected void setDisplayMessage(String string) {
		// TODO :)
	}

	public void showNotification(String message) {
		// Show toast message
		Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
	}

	public void sendVibration(long [] vibrate) {
		// Create our Notification Manager:
		NotificationManager gNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		if(gNotificationManager != null)
		{
			// Create our Notification that will do the vibration:
			Notification gNotification = new Notification();
			// Set the vibration:
			gNotification.vibrate = vibrate;
			// send vibration :)
			gNotificationManager.notify(1, gNotification);
		}
	}

	final Handler mScanProgressHandler = new Handler() {
	    @Override
		public void handleMessage(Message msg) {
	        switch (msg.what) {
	        case SCANPROGRESS_START:
	        	if(_bInScanMode)
	        		break;
	        	
	        	if(m_mScanProgressDialog != null)
	        		m_mScanProgressDialog.dismiss();
            	m_mScanProgressDialog = ProgressDialog.show(RobotCore_Activity.this, "Scanning", "wait...", true);

	            _bInScanMode = true;
	            break;
	        case SCANPROGRESS_MSG:
	            if (m_mScanProgressDialog!=null && m_mScanProgressDialog.isShowing()) {
	            	m_mScanProgressDialog.setMessage(((String) msg.obj));
	            }
	            break;
	        case SCANPROGRESS_FINISH:
	            if (m_mScanProgressDialog!=null && m_mScanProgressDialog.isShowing()) {
	            	m_mScanProgressDialog.dismiss();
	            	m_mScanProgressDialog = null;
	            }
	            _bInScanMode = false;
	            break;
	        }
	    }
	};

   private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            // When discovery finds a device
            if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
				_states._bBluetoothOnline = false;
				setDisplayMessage("Disconnected...");            	
            }
        }
    };

    class myIPServer extends IPServer
	{
    	myIPServer(IPEvent_Server events)
    	{
    		super(null);
    	}
    	
    	@Override
    	public IPEvent_Server createServerEvents()
    	{
    		return new IPServerCommandEvents()
				{
				@Override
				public void onConnected() {
					_states._bIPOnline = true;
				}
				@Override
				public void onDisconnected() {
					_states._bIPOnline = false;
				}
				@Override
				public void onError(String sError) {
				}
				@Override
				public void onText(String sText) {
				}
				@Override
				public void onHello(String sVersion, String sControllerName) {
					sendMessage(IPServerCommandProtocol.sendHello(m_sCoreName, m_sCoreVersion));
				}
				@Override
				public void onAskVersion() {
					sendMessage(IPServerCommandProtocol.sendVersion(m_sCoreName, m_sCoreVersion));
				}
				@Override
				public void onAskSetting() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskSetting());
				}
				@Override
				public void onAskSettingBody() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskSettingBody());
				}
				@Override
				public void onAskSettingTurret() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskSettingTurret());
				}
				@Override
				public void onAskSettingRay() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskSettingRay());
				}
				@Override
				public void onSetSettingRayCount(int iCount) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSettingRayCount(iCount));
				}
				@Override
				public void onSetSettingRayDelay(int iDelay) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSettingRayDelay(iDelay));
				}
				@Override
				public void onSetSettingRaySensors(int sSensors) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSettingRaySensors(sSensors));
				}
				@Override
				public void onSetSettingRayPrecision(float fPrecision) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSettingRayPrecision(fPrecision));
				}
				@Override
				public void onSetSettingRay(int iCount, int iDelay, int sSensors,
						float fPrecision) {
					//m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSettingRayRay(iCount, iDelay, sSensors, fPrecision));
				}
				@Override
				public void onAskStatus() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskStatus());
				}
				@Override
				public void onAskStatusSensors() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskStatusSensors());
				}
				@Override
				public void onAskStatusTurret() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskStatusTurret());
				}
				@Override
				public void onAskStatusBody() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskStatusBody());
				}
				@Override
				public void onSetSendStatus(boolean bSet) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSendStatus(bSet));
				}
				@Override
				public void onReset() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendReset());
				}
				@Override
				public void onResetBody() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendResetBody());
				}
				@Override
				public void onResetTurret() {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendResetTurret());
				}
				@Override
				public void onBodyFront(int iSpeed) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendBodyFront(iSpeed));
				}
				@Override
				public void onBodyBack(int iSpeed) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendBodyBack(iSpeed));
				}
				@Override
				public void onBodyLeft(int iSpeed) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendBodyLeft(iSpeed));
				}
				@Override
				public void onBodyRight(int iSpeed) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendBodyRight(iSpeed));
				}
				@Override
				public void onBodyMoveToTarget(int iSpeed, boolean bFront, int iLen) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendBodyMoveToTarget(iSpeed, bFront, iLen));
				}
				@Override
				public void onBodyRotateToTarget(int iSpeed, int iBearing) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendBodyRotateToTarget(iSpeed, iBearing));
				}
				@Override
				public void onTurretSetPosition(short low, short high) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendTurretSetPosition(low, high));
				}
				@Override
				public void onTurretScanPosition(short low, short high) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendTurretScanPosition(low, high));
				}
				@Override
				public void onTurretScanLine(short low_beg, short low_end, short low_step,
						short high) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendTurretScanLine(low_beg, low_end, low_step, high));
				}
				@Override
				public void onTurretScanZone(short low_beg, short low_end, short low_step,
						short high_beg, short high_end, short high_step) {
					m_SPPClient.sendMessage(BluetoothCommandProtocol.sendTurretScanZone(low_beg, low_end, low_step, high_beg, high_end, high_step));
				}
				@Override
				public void onAskPosition() {
     				PositionMarker pos = m_PositionSensor.pos();
    				
    				if(pos != null)
    					sendMessage(IPServerCommandProtocol.sendPosition(pos));
				}
				@Override
				public void onAskCameraImage() {
					sendMessage(IPServerCommandProtocol.sendCameraImage(m_viewCamera.getCamera().getBuffer()));
				}
				
				@Override
				public void onAskVibrate(long [] datas) {
         			sendVibration(datas);
				}
			};
		};		
	}

	final BluetoothCommandEvents mBTEvents = new BluetoothCommandEvents()
	{
		@Override
		public void onConnected()
		{
			Toast.makeText(getApplicationContext(), "Connected", Toast.LENGTH_SHORT).show();
			_states._bBluetoothOnline = true;
			setDisplayMessage("Online...");
			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskVersion()); // ask version
			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskSetting()); // check report sending.
			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskSettingBody()); // get body setting
			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskSettingTurret()); // get turret setting
			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskSettingRay()); // get ray setting
		}
		@Override
		public void onDisconnected()
		{
        	Toast.makeText(getApplicationContext(), "Disconnected", Toast.LENGTH_SHORT).show();
        	_states._bBluetoothOnline = false;
        	_states._bReady = false;
		}
		@Override
		public void onError(String sError)
		{
        	Toast.makeText(getApplicationContext(), "Get Error : " + sError, Toast.LENGTH_LONG).show();
       	}
		@Override
		public void onText(String sText)
		{
        	Toast.makeText(getApplicationContext(), "Get text : " + sText, Toast.LENGTH_LONG).show();
		}
		@Override
		public void onVersion(String robotname, String robotversion)
		{
			_iReadyStep |= 0x01;
			
			_settings._sRobot_Name = robotname;
			_settings._sRobot_Version = robotversion;
			
			isReady();
		}
		@Override
		public void onSettings(boolean bReport_Transmission) {
			_states._bReport_Transmission =  bReport_Transmission;
			_iReadyStep |= 0x02;
			
			isReady();
		}
		@Override
		public void onBodySettings(int iBody_MinSpeed, int iBody_MidSpeed,
				int iBody_MaxSpeed, int iBody_MinDist, int iBody_MaxDist) {
			_settings._iBody_MinSpeed =  iBody_MinSpeed;
			_settings._iBody_MidSpeed =  iBody_MidSpeed;
			_settings._iBody_MaxSpeed =  iBody_MaxSpeed;
			_settings._iBody_MinDist =  iBody_MinDist;
			_settings._iBody_MaxDist =  iBody_MaxDist;
		
			_iReadyStep |= 0x04;
			
			isReady();
		}
		@Override
		public void onTurretSettings(int iTurret_MinLow, int iTurret_MidLow,
				int iTurret_MaxLow, int iTurret_MinHigh, int iTurret_MidHigh,
				int iTurret_MaxHigh, int iTurret_MinDist, int iTurret_MidDist,
				int iTurret_MaxDist) {
			_settings._iTurret_MinLow = iTurret_MinLow;
			_settings._iTurret_MidLow = iTurret_MidLow;
			_settings._iTurret_MaxLow = iTurret_MaxLow;
			_settings._iTurret_MinHigh = iTurret_MinHigh;
			_settings._iTurret_MidHigh = iTurret_MidHigh;
			_settings._iTurret_MaxHigh = iTurret_MaxHigh;
			_settings._iTurret_MinDist = iTurret_MinDist;
			_settings._iTurret_MidDist = iTurret_MidDist;
			_settings._iTurret_MaxDist = iTurret_MaxDist;
			_iReadyStep |= 0x08;
		
			isReady();
		}
		@Override
		public void onRaySettings(int iRay_Count, int iRay_Delay, int iRay_Sensors, float fRay_Precision)
		{
			_settings._iRay_Count = iRay_Count;
			_settings._iRay_Delay = iRay_Delay;
			_settings._iRay_Sensors = iRay_Sensors;
			_settings._fRay_Precision = fRay_Precision;
			_iReadyStep |= 0x10;
			
			isReady();
		}
		@Override
		public void onHitSensors(boolean bBody_Switch_BackLeft,
				boolean bBody_Switch_BackCenter, boolean bBody_Switch_BackRight) {
			_states._bBody_Switch_BackLeft = bBody_Switch_BackLeft;
			_states._bBody_Switch_BackCenter = bBody_Switch_BackCenter;			        			
			_states._bBody_Switch_BackRight = bBody_Switch_BackRight;
		}
		@Override
		public void onLatestDistance(float fBody_LastMoveDistance_Left,
				float fBody_LastMoveDistance_Right) {
			_states._fBody_LastMoveDistance_Left = fBody_LastMoveDistance_Left;
			_states._fBody_LastMoveDistance_Right = fBody_LastMoveDistance_Right;
		}
	    @Override
		public void onTurretInformation(int iTurret_Low, int iTurret_High,
				int iTurret_Distance) {
			_states._iTurret_Low = iTurret_Low;
			_states._iTurret_High = iTurret_High;
			_states._iTurret_Distance = iTurret_Distance;
		}
	    @Override
		public void onBodyInformation(float fBody_Bear, float fBody_Pitch,
				float fBody_Roll, int iBody_Distance) {
			_states._fBody_Bear =  fBody_Bear;
			_states._fBody_Pitch = fBody_Pitch;
			_states._fBody_Roll = fBody_Roll;
			_states._iBody_Distance = iBody_Distance;
		}
		@Override
		public void onScan(ScanResult scan) {
			mScanProgressHandler.sendMessage(mScanProgressHandler.obtainMessage(SCANPROGRESS_START, null));
			mScanProgressHandler.sendMessage(mScanProgressHandler.obtainMessage(SCANPROGRESS_MSG, "Analysing datas..."));

			_latest_scan = scan;
			
    		mScanProgressHandler.sendMessage(mScanProgressHandler.obtainMessage(SCANPROGRESS_FINISH, null));
		}	    
		
		private void isReady()
		{
    		if(_states._bReady == false && _iReadyStep == (0x01|0x02|0x04|0x08|0x10))
    		{
    			_states._bReady = true;
    			setDisplayMessage("");
    			if(!_states._bReport_Transmission)
        			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSendStatus(true));

    			// force ray settings !
    			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSettingRayCount(10));
    			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSettingRayDelay(5));
    			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSettingRayPrecision(0.10f));
    			m_SPPClient.sendMessage(BluetoothCommandProtocol.sendSetSettingRaySensors(3));
    		}
    		else if(_states._bReady == false)
    		{
    			String sWait = "Wait ";
    			sWait += ((_iReadyStep & 0x01) == 0x01)?"*":".";
    			sWait += ((_iReadyStep & 0x02) == 0x02)?"*":".";
    			sWait += ((_iReadyStep & 0x04) == 0x04)?"*":".";
    			sWait += ((_iReadyStep & 0x08) == 0x08)?"*":".";
    			sWait += ((_iReadyStep & 0x10) == 0x10)?"*":".";
    			setDisplayMessage(sWait);
    		}
		}
	};
}
